﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace TTEngine
{
    public class DataInfo
    {
        public UInt32 length;
        public HashType hashType;
        public byte[] hash;//srcHash 然后不固定长度,根据HashType决定
        public string GetHashStr()
        {
            if (hashType == HashType.None)
                return null;
            string str = "";
            for(var i=0;i<32;i++)
            {
                str += hash[i].ToString("X02");
            }
            return str;
        }
        public virtual int ComputeLength()
        {
            return 4 + 1 + (hashType == HashType.None ? 0 : 32);
        }
        public bool EqualWith(DataInfo another)
        {
            if (another == null && this != null)
                return false;
            if (another == this)
                return true;
            if (another.length != this.length)
                return false;
            if (another.hashType != this.hashType)
                return false;
            if (this.hashType == HashType.SHA256)
            {
                for (var i = 0; i < 32; i++)
                {
                    if (this.hash[i] != another.hash[i])
                        return false;
                }
            }
            return true;
        }
    }
    public enum HashType : byte
    {
        None,//0字节
        SHA256,//64字节
    }
    public enum AssetPlatform : byte
    {
        Cross,//跨平台资源
        Windows = 0x10,
        Linux = 0x20,
        Android = 0x28,
    }
    public class AssetDataInfo : DataInfo
    {
        public AssetPlatform platform;
        public UInt32 dataOffset;
        public override int ComputeLength()
        {
            return base.ComputeLength() + 1 + 4;
        }
    }
    public class AssetHead
    {
        public string MagicHead;// = "TTA";
        public byte version;// = 1;
        public UInt32 AssetType;//资源类型
        public DataInfo srcfileInfo;
        public AssetDataInfo[] AssetDataInfo;//实际保存的数据
        public byte[] ConvertOption;//限制64K长度
        public int ComputeLength()
        {
            int len = 3 + 1 + 4  //MagicHead Version AssetType
               + srcfileInfo.ComputeLength()
                + 1;//size of assetdatainfo
            for (var i = 0; i < AssetDataInfo.Length; i++)
            {
                len += AssetDataInfo[i].ComputeLength();
            }
            len += 2 + (ConvertOption != null ? ConvertOption.Length : 0);
            return len;
        }
    }


    /// <summary>
    /// 资源存取工具
    /// </summary>
    public static class AssetDataTool

    {
        [ThreadStatic]
        static SHA256 __sha256;
        static SHA256 sha256
        {
            get
            {
                if (__sha256 == null)
                    __sha256 = SHA256.Create();
                return __sha256;
            }
        }
        public static void WriteAssetHead(System.IO.Stream stream, AssetHead head)
        {
            byte[] headbuf = new byte[4];
            headbuf[0] = (byte)head.MagicHead[0];
            headbuf[1] = (byte)head.MagicHead[1];
            headbuf[2] = (byte)head.MagicHead[2];
            headbuf[3] = head.version;
            stream.Write(headbuf, 0, 4);
            byte[] typebuf = BitConverter.GetBytes(head.AssetType);
            stream.Write(typebuf, 0, 4);
            WriteDataInfo(stream, head.srcfileInfo);
            stream.WriteByte((byte)head.AssetDataInfo.Length);
            for (var i = 0; i < head.AssetDataInfo.Length; i++)
            {
                var data = head.AssetDataInfo[i];
                stream.WriteByte((byte)data.platform);
                byte[] offbuf = BitConverter.GetBytes(data.dataOffset);
                stream.Write(offbuf, 0, 4);
                WriteDataInfo(stream, data);

            }
            byte[] offoplen = BitConverter.GetBytes(head.ConvertOption == null ? (UInt16)0 : (UInt16)head.ConvertOption.Length);
            stream.Write(offoplen, 0, 2);
            if (head.ConvertOption != null)
                stream.Write(head.ConvertOption, 0, head.ConvertOption.Length);
        }
        static void WriteDataInfo(System.IO.Stream stream, DataInfo datainfo)
        {
            byte[] lenbuf = BitConverter.GetBytes(datainfo.length);
            stream.Write(lenbuf, 0, 4);
            stream.WriteByte((byte)datainfo.hashType);
            if (datainfo.hashType == HashType.None)
            {

            }
            else if (datainfo.hashType == HashType.SHA256)
            {
                stream.Write(datainfo.hash, 0, 32);
            }
        }
        static void ReadDataInfo(System.IO.Stream stream, DataInfo datainfo)
        {
            byte[] buf = new byte[4];
            stream.Read(buf, 0, 4);
            datainfo.length = BitConverter.ToUInt32(buf, 0);
            datainfo.hashType = (HashType)stream.ReadByte();
            if (datainfo.hashType == HashType.None)
            {

            }
            else if (datainfo.hashType == HashType.SHA256)
            {
                datainfo.hash = new byte[32];
                stream.Read(datainfo.hash, 0, 32);
            }
        }
        public static DataInfo ComputeDataInfo(byte[] data)
        {
            var bytes = data;
            DataInfo info = new DataInfo();
            info.length = (UInt32)bytes.Length;
            info.hashType = HashType.SHA256;
            info.hash = sha256.ComputeHash(bytes);
            return info;
        }

        public static AssetDataInfo ComputeAssetDataInfo(byte[] data, AssetPlatform platform, UInt32 offset)
        {
            AssetDataInfo info = new AssetDataInfo();
            info.length = (UInt32)data.Length;
            info.hashType = HashType.SHA256;
            info.hash = sha256.ComputeHash(data);
            info.platform = platform;
            info.dataOffset = offset;
            return info;
        }
        public static bool ReadAssetHead(System.IO.Stream stream, AssetHead head)
        {
            byte[] buf = new byte[4];
            stream.Read(buf, 0, 4);
            head.MagicHead = System.Text.Encoding.ASCII.GetString(buf, 0, 3);
            if (head.MagicHead != "TTA")
                return false;
            head.version = buf[3];
            if (head.version < 0)
                return false;
            if (head.version > 1)
                return false;
            stream.Read(buf, 0, 4);
            head.AssetType = BitConverter.ToUInt32(buf, 0);
            head.srcfileInfo = new DataInfo();
            ReadDataInfo(stream, head.srcfileInfo);

            var platformcount = stream.ReadByte();
            head.AssetDataInfo = new AssetDataInfo[platformcount];
            for (var i = 0; i < platformcount; i++)
            {
                AssetDataInfo data = new AssetDataInfo();
                data.platform = (AssetPlatform)stream.ReadByte();
                stream.Read(buf, 0, 4);
                data.dataOffset = BitConverter.ToUInt32(buf, 0);
                ReadDataInfo(stream, data);
                head.AssetDataInfo[i] = data;
            }
            stream.Read(buf, 0, 2);
            var oplen = BitConverter.ToUInt16(buf, 0);
            head.ConvertOption = new byte[oplen];
            stream.Read(head.ConvertOption, 0, oplen);
            return true;
        }

    }
}
